module variable_mod

use global_mod,       only: ink, rlk
use utils_misc_mod,   only: Critical_Error, ARROW_ERR_PAR_INVAL
use memory_mod, only: Allocate

implicit none

integer(kind=ink), parameter :: ARROW_BASE_NULL = 0
integer(kind=ink), parameter :: ARROW_BASE_CELL = 1
integer(kind=ink), parameter :: ARROW_BASE_NODE = 2

integer(kind=ink), parameter :: ARROW_VAR_NULL  = 0
integer(kind=ink), parameter :: ARROW_VAR_REAL  = 1
integer(kind=ink), parameter :: ARROW_VAR_INT   = 2
integer(kind=ink), parameter :: ARROW_VAR_LOG   = 3


type VarObj
  logical :: zinit = .false.
  character(len=128) :: name = ""
  integer(kind=ink)  :: class = ARROW_VAR_NULL
  integer(kind=ink)  :: base  = ARROW_BASE_NULL
  real(kind=rlk), pointer :: rsdata(:)   => NULL()
  real(kind=rlk), pointer :: rvdata(:,:) => NULL()
  real(kind=rlk), pointer :: isdata(:)   => NULL()
  real(kind=rlk), pointer :: ivdata(:,:) => NULL()
end type

type VarListObj
  integer(kind=ink) :: nvars = 0
  character(len=128), pointer :: names(:)
  type(VarObj),       pointer :: vars(:)
end type

contains

!> Create a variable
function Var_Create(name, class, base, nn, mm) result(var)
  character(len=128), intent(in) :: name
  integer(kind=ink),  intent(in) :: class
  integer(kind=ink),  intent(in) :: base
  integer(kind=ink),  intent(in) :: nn
  integer(kind=ink),  optional   :: mm
  type(VarObj) :: var
  select case(class)
    case(ARROW_VAR_REAL)
      if (present(mm)) then
	call Allocate(var%rvdata, nn, mm)
      else
	call Allocate(var%rsdata, nn)
      endif
    case(ARROW_VAR_INT)
      if (present(mm)) then
	call Allocate(var%ivdata, nn, mm)
      else
	call Allocate(var%isdata, nn)
      endif
    case default
      call Critical_Error(ARROW_ERR_PAR_INVAL, "Var_Create")
  end select
  var%zinit = .true.
end function Var_Create


subroutine VarList_Expand(varlist, nnew)
  type(VarListObj),   intent(inout) :: varlist
  integer(kind=ink),  intent(in)    :: nnew
  integer(kind=ink) :: n1, n2

  n1 = varlist%nvars
  n2 = n1+nnew

  allocate(varlist%names(nnew))
  allocate(varlist%vars(nnew))

end subroutine VarList_Expand


!> Add a variable
subroutine VarList_AddVar(varlist, name, class, base, nn, mm)
  type(VarListObj),   intent(inout) :: varlist
  character(len=128), intent(in)    :: name
  integer(kind=ink),  intent(in)    :: class
  integer(kind=ink),  intent(in)    :: base
  integer(kind=ink),  intent(in)    :: nn
  integer(kind=ink),  optional      :: mm
  type(VarObj) :: var
  if (present(mm)) then
    var = Var_Create(name, class, base, nn, mm)
  else
    var = Var_Create(name, class, base, nn)
  endif

end subroutine VarList_AddVar

end module variable_mod
